<script lang="ts">
    import Portal from "svelte-portal";
    import type { InnerPopupProps } from "./InnerPopup";
    import usePortal from "../../utils/usePortal";
    import { useClassList } from "../../utils/useClassList";
    import { getRandomIntInclusive, isColor } from "../../utils/utils";
    import { onMount } from "svelte";
    import usezIndex from "../../utils/usezIndex";
    import useAlignPostion from "../../utils/useAlignPostion";
    import { useStyle } from "../../utils/useStyle";
    import { fade } from "svelte/transition";
    import { useClickOutside } from "../../utils/useClickOutside";
    import { Space } from "../../Space";
    import { Button } from "../../Button";
    import { _ } from "svelte-i18n";

    let { visible = $bindable(), ...props }: InnerPopupProps = $props();
    let inner: HTMLElement | undefined = $state();
    let wrap = $state();
    const clsPrefix = props.clsPrefix ?? "cm-popover";
    const varName = props.varName ?? "popover";
    const id = `${clsPrefix}-portal`;
    let nextElementSibling: any;
    const trigger = props.confirm ? "click" : props.trigger || "hover";
    const align = props.align || "top";
    const offset = $derived(props.offset || 0);
    let buttonLoading = $state(false);
    let _update = $state(getRandomIntInclusive(1, 10000000));

    const color = isColor(props.theme) ? "" : props.theme;
    const classList = $derived(
        useClassList(props, `${clsPrefix}-inner`, {
            [`${clsPrefix}-with-arrow`]: props.arrow,
            [`${clsPrefix}-with-arrow-center`]: props.arrowPointAtCenter,
            [`${clsPrefix}-confirm`]: props.confirm,
            [`${clsPrefix}-${color}`]: color,
        })
    );
    const zindex = usezIndex();

    const posStyle = $derived.by(() => {
        visible;
        _update;
        if (nextElementSibling) {
            let placement = align;
            if (props.arrowPointAtCenter) {
                if (["top", "topLeft", "topRight"].includes(placement)) {
                    placement = "top";
                }
                if (["bottom", "bottomLeft", "bottomRight"].includes(placement)) {
                    placement = "bottom";
                }
                if (["left", "leftTop", "leftBottom"].includes(placement)) {
                    placement = "left";
                }
                if (["right", "rightTop", "rightBottom"].includes(placement)) {
                    placement = "right";
                }
            }
            const pos: any = useAlignPostion(placement, nextElementSibling);
            pos.top = pos.top + document.documentElement.scrollTop + "px";
            pos.left = pos.left + document.documentElement.scrollLeft + "px";
            pos["z-index"] = zindex;
            Object.assign(pos, {
                [`--cui-${varName}-background-color`]: isColor(props.theme) ? props.theme : "",
                [`--cui-${varName}-offset`]: `${offset}px`,
            });
            return useStyle(props, pos);
        }
    });

    $effect(() => {
        removeClickOutside?.();
        wrap;
        if (trigger === "click" && nextElementSibling && wrap) {
            removeClickOutside = useClickOutside([wrap, nextElementSibling], () => {
                if (buttonLoading) {
                    return;
                }
                visible = false;
            });
        }
    });

    let timer: any = null;
    const hideDelay = props.hideDelay || 200;
    const clearDelayTimer = () => {
        if (timer) {
            clearTimeout(timer);
            timer = null;
        }
    };

    const onPopMouseEnter = () => {
        if (trigger === "hover") {
            clearDelayTimer();
        }
    };

    const onPopMouseLeave = () => {
        onMouseLeave();
    };

    const onMouseLeave = () => {
        if (props.disabled) {
            return;
        }
        if (trigger === "hover") {
            timer = setTimeout(() => {
                visible = false;
                props.onvisiblechange?.(false);
            }, hideDelay);
        }
    };

    const onMouseEnter = () => {
        if (props.disabled) {
            return;
        }
        if (trigger === "hover") {
            clearDelayTimer();
            visible = true;
            props.onvisiblechange?.(true);
        }
    };

    const onFocus = () => {
        if (props.disabled) {
            return;
        }
        clearDelayTimer();
        if (trigger === "focus") {
            visible = true;
            props.onvisiblechange?.(true);
        }
    };

    const onBlur = () => {
        if (trigger === "focus") {
            timer = setTimeout(() => {
                visible = false;
                props.onvisiblechange?.(false);
            }, hideDelay);
        }
    };

    const onClick = (e: any) => {
        if (props.disabled) {
            return;
        }
        e.preventDefault();
        e.stopPropagation();
        if (trigger === "click") {
            const show = visible;
            visible = !show;
            props.onvisiblechange?.(!show);
        }
    };

    const onOk = async () => {
        buttonLoading = true;
        const ret = await props.onok?.();
        buttonLoading = false;
        if (ret === undefined || ret === true) {
            visible = false;
            props.onvisiblechange?.(false);
        }
    };

    const onCancel = () => {
        props.oncancel?.();
        visible = false;
        props.onvisiblechange?.(false);
    };

    let removeClickOutside: () => void;
    onMount(() => {
        nextElementSibling = props.parent ? inner?.parentElement : inner?.nextElementSibling;
        if (!nextElementSibling) {
            console.error("InnerPopup must be used after a trigger element");
        }
        inner && inner.parentNode?.removeChild(inner);
        if (nextElementSibling) {
            if (trigger === "hover") {
                nextElementSibling.addEventListener("mouseenter", onMouseEnter, false);
                nextElementSibling.addEventListener("mouseleave", onMouseLeave, false);
            }
            if (trigger === "click") {
                nextElementSibling.addEventListener("click", onClick, false);
            }
            if (trigger === "focus") {
                nextElementSibling.addEventListener("focus", onFocus, false);
                nextElementSibling.addEventListener("blur", onBlur, false);
            }
        }
    });

    export const update = () => {
        _update = getRandomIntInclusive(1, 10000000);
    };
</script>

<span style="display: none" bind:this={inner}></span>
{@render props.children?.()}
<Portal target={usePortal(id, id)}>
    {#if visible}
        <div
            bind:this={wrap}
            style={posStyle}
            in:fade={{ duration: 250 }}
            out:fade={{ duration: 250 }}
            x-placement={align}
            class={classList}
            onmouseenter={onPopMouseEnter}
            onmouseleave={onPopMouseLeave}
            role="tooltip"
        >
            {#if props.title}
                <div class={`${clsPrefix}-title`}>
                    {#if typeof props.title === "function"}
                        {@render props.title?.()}
                    {:else}
                        {props.title}
                    {/if}
                </div>
            {/if}
            <div class={`${clsPrefix}-body`} style={props.contentStyle}>
                {#if typeof props.content === "function"}
                    {@render props.content?.()}
                {:else}
                    {props.content}
                {/if}
            </div>
            {#if props.confirm}
                <Space class={`${clsPrefix}-tools`} justify="end">
                    {#if props.showCancel}
                        <Button type={props.cancelType || "default"} size="small" onclick={onCancel}>{props.cancelText ?? $_("Popover.cancelText")}</Button>
                    {/if}
                    <Button type={props.okType || "primary"} size="small" onclick={onOk} loading={buttonLoading}>{props.okText ?? $_("Popover.okText")}</Button>
                </Space>
            {/if}
            {#if props.arrow}
                <svg width="24" height="8" xmlns="http://www.w3.org/2000/svg" class={`${clsPrefix}-icon-arrow`}>
                    <path d="M0.5 0L1.5 0C1.5 4, 3 5.5, 5 7.5S8,10 8,12S7 14.5, 5 16.5S1.5,20 1.5,24L0.5 24L0.5 0z" opacity="1" />
                    <path d="M0 0L1 0C1 4, 2 5.5, 4 7.5S7,10 7,12S6 14.5, 4 16.5S1,20 1,24L0 24L0 0z" />
                </svg>
            {/if}
        </div>
    {/if}
</Portal>
